##此模块包含了预约表的增删查改和患者预约
from datetime import datetime

from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import and_, or_
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session

from database import get_db
from models.payment_model import Payment
from schemas.payment_schemas import PaymentType, PaymentStatus
from services.auth_service import require_user
from models.user_models import *
from schemas.booking_schemas import *
from typing import List, Dict
from models.booking_model import DocSchedule, Booking

from sqlalchemy.orm import joinedload
from sqlalchemy.sql import exists

from sqlalchemy.exc import OperationalError, DBAPIError

from services.datetime_service import now_time

router = APIRouter()

max_days=7

@router.get("/bookings/available_dates")
def get_available_dates():
    return {"max_days":max_days}


### 预约表的增删改查(admin)
@router.get("/bookings", response_model=List[Dict])
def get_all_bookings(db: Session = Depends(get_db), user: Booking = Depends(require_user(UserType.ADMIN))):
    bookings = db.query(Booking).all()
    return [booking.to_dict() for booking in bookings]

@router.get("/bookings/{booking_id}", response_model=Dict)
def get_booking(booking_id: int, db: Session = Depends(get_db), user: Booking = Depends(require_user(UserType.ADMIN))):
    booking = db.query(Booking).filter(Booking.ID == booking_id).first()
    if not booking:
        raise HTTPException(status_code=404, detail="Booking not found")
    return booking.to_dict()


##增加预约记录，此处是针对管理员的
@router.post("/bookings", response_model=Dict)
def create_booking(
    booking: BookingCreate, 
    db: Session = Depends(get_db), 
    user: dict = Depends(require_user(UserType.ADMIN))
):
    try:
        booking.check()
        time_period = booking.book_time.value  # 获取AM/PM
        
        # 查询对应医生的排班记录
        schedule = db.query(DocSchedule).filter(
            DocSchedule.doctor_id == booking.doctor_ID,
            DocSchedule.schedule_date == booking.book_date,
            DocSchedule.department == booking.department
        ).first()
        
        if not schedule:
            raise HTTPException(status_code=404, detail="医生排班记录不存在")
        
        # 检查号源余量
        remaining = schedule.am_remaining if time_period == "AM" else schedule.pm_remaining
        if remaining <= 0:
            raise HTTPException(status_code=400, detail="该时段号源已满")
        
        # 创建预约记录
        db_booking = Booking(
            patient_ID=booking.patient_ID,
            book_date=booking.book_date,
            book_time=time_period,
            doctor_ID=booking.doctor_ID,
            department=booking.department,
            whether_done="N"
        )
        
        # 减少对应号源
        if time_period == "AM":
            schedule.am_remaining -= 1
        else:
            schedule.pm_remaining -= 1
        
        db.add(db_booking)
        db.commit()
        db.refresh(db_booking)
        return db_booking.to_dict()
        
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except IntegrityError:
        db.rollback()
        raise HTTPException(status_code=400, detail="数据冲突，请检查患者/医生ID")
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=f"创建预约失败: {str(e)}")



@router.put("/bookings/{booking_id}", response_model=Dict)
def update_booking(booking_id: int, booking_update: BookingUpdate, db: Session = Depends(get_db), user: Booking = Depends(require_user(UserType.ADMIN))):
    try:
        booking_update.check()
        db_booking = db.query(Booking).filter(Booking.ID == booking_id).first()
        if not db_booking:
            raise HTTPException(status_code=404, detail="Booking not found")
        
        update_data = booking_update.dict(exclude_unset=True)
        for key, value in update_data.items():
            setattr(db_booking, key, value)
        
        db.commit()
        db.refresh(db_booking)
        return db_booking.to_dict()
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


#管理员删除预约记录
@router.delete("/bookings/{booking_id}")
def delete_booking(
    booking_id: int, 
    db: Session = Depends(get_db), 
    user: dict = Depends(require_user(UserType.ADMIN))
):
    try:
        booking = db.query(Booking).filter(Booking.ID == booking_id).first()
        if not booking:
            raise HTTPException(status_code=404, detail="预约记录不存在")
        
        # 查询对应排班记录
        schedule = db.query(DocSchedule).filter(
            DocSchedule.doctor_id == booking.doctor_ID,
            DocSchedule.schedule_date == booking.book_date,
            DocSchedule.department == booking.department
        ).first()
        
        if not schedule:
            raise HTTPException(status_code=404, detail="关联的排班记录不存在")
        
        # 归还号源
        if booking.book_time == "AM":
            schedule.am_remaining += 1
        elif booking.book_time == "PM":
            schedule.pm_remaining += 1

        # 删除对应的支付记录（如果存在）
        payment = db.query(Payment).filter(
            Payment.type == PaymentType.BOOK,
            Payment.ref_ID == booking.ID
        ).first()

        if payment:
            db.delete(payment)
        
        # 删除预约
        db.delete(booking)
        db.commit()
        return {"message": "预约删除成功，号源已归还"}
        
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=f"删除失败: {str(e)}")


@router.post("/patient_bookings", response_model=Dict)
def create_patient_booking(
    booking: PatientBookingCreate,
    db: Session = Depends(get_db),
    user: dict = Depends(require_user(UserType.PATIENT, UserType.ADMIN))
):
    try:
        if user["user_type"] == UserType.PATIENT and user['user_ref_id'] != booking.patient_id:
            raise HTTPException(status_code=403, detail="传入患者ID与Token不一致")

        # 数据验证
        booking.check()
        time_period = "AM" if booking.time_period == "上午" else "PM"

        # 开始事务，悲观锁保护排班记录
        with db.begin():
            schedule = db.query(DocSchedule).with_for_update().filter(
                DocSchedule.ID == booking.schedule_id
            ).first()

            if not schedule:
                raise HTTPException(status_code=404, detail="排班记录不存在")

            # 检查余号
            remaining = schedule.am_remaining if time_period == "AM" else schedule.pm_remaining
            if remaining <= 0:
                raise HTTPException(status_code=400, detail="没有余号了")

            # 创建预约记录
            db_booking = Booking(
                patient_ID=booking.patient_id,
                book_date=schedule.schedule_date,
                book_time=time_period,
                doctor_ID=schedule.doctor_id,
                department=schedule.department,
                whether_done="N"
            )

            # 更新余号
            if time_period == "AM":
                schedule.am_remaining -= 1
            else:
                schedule.pm_remaining -= 1

            db.add(db_booking)
            db.flush()  # 保证 db_booking.ID 可用但不提交

            # 创建支付记录
            db_payment = Payment(
                patient_ID=booking.patient_id,
                con_date=now_time().date(),
                expense=5000,  # 固定50元预约费
                type=PaymentType.BOOK,
                pay_y_n=PaymentStatus.N,
                ref_ID=db_booking.ID  # 预约ID作为引用
            )

            db.add(db_payment)
            # with db.begin() 块中不需要手动commit

        # 刷新对象以获取数据库生成的ID等
        db.refresh(db_booking)
        return {
            "booking": db_booking.to_dict(),
            "payment": db_payment.to_dict()
        }

    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except IntegrityError as e:
        db.rollback()
        if 'foreign key constraint' in str(e).lower():
            raise HTTPException(status_code=400, detail="患者或医生ID无效")
        raise HTTPException(status_code=400, detail="数据冲突，请检查输入")
    except (OperationalError, DBAPIError) as e:
        db.rollback()
        if 'lock wait timeout' in str(e).lower():
            raise HTTPException(status_code=429, detail="系统繁忙，请稍后重试")
        raise HTTPException(status_code=500, detail="数据库操作错误")
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=f"服务器内部错误: {str(e)}")


    

##患者输入自身ID查询所有有关预约记录
##患者输入自身ID查询所有有关预约记录
@router.get("/patient_bookings/{patient_id}", response_model=List[PatientBookingResponse])
def get_patient_bookings(
        patient_id: int,
        db: Session = Depends(get_db),
        user: dict = Depends(require_user(UserType.PATIENT, UserType.ADMIN))
):
    try:
        # 检查权限：患者只能查看自己的记录
        if user["user_type"] == UserType.PATIENT.value and user["user_ref_id"] != patient_id:
            raise HTTPException(status_code=403, detail="无权查看其他患者的预约记录")

        # 查询预约记录并左连接支付记录，以便获取支付状态
        bookings_and_payments = (
            db.query(Booking, Payment)
            .options(joinedload(Booking.doctor)) # 预加载医生信息
            .outerjoin(Payment, and_(Payment.ref_ID == Booking.ID, Payment.type == PaymentType.BOOK))
            .filter(Booking.patient_ID == patient_id)
            .all()
        )

        response = []
        for booking, payment in bookings_and_payments:
            # 根据是否有支付记录以及支付记录的状态来确定 status 字段
            if payment and payment.pay_y_n == PaymentStatus.Y:
                payment_status_str = "已支付"
                payment_ID=payment.ID
            else:
                payment_status_str = "未支付"
                payment_ID=-1

            response.append(
                PatientBookingResponse(
                    id=booking.ID,
                    book_date=booking.book_date,
                    book_time=booking.book_time,
                    doctor_id=booking.doctor_ID,
                    doctor_name=booking.doctor.name if booking.doctor else "未知医生",
                    doctor_image_url=booking.doctor.image_url if booking.doctor else "",
                    department=booking.department,
                    status=payment_status_str,  # 使用 status 字段表示支付状态
                    ref_ID=payment_ID,
                    whether_done=booking.whether_done
                )
            )
        return response

    except Exception as e:
        # 捕获更具体的异常，例如数据库查询失败、数据不存在等，以便更好地调试
        raise HTTPException(status_code=500, detail=f"获取预约记录失败: {str(e)}")

@router.delete("/patient_bookings/{booking_id}", response_model=CancelBookingResponse)
def cancel_patient_booking(
    booking_id: int,
    db: Session = Depends(get_db),
    user: dict = Depends(require_user(UserType.PATIENT, UserType.ADMIN))
):
    try:
        with db.begin():
            # 锁定预约记录，避免并发删除冲突
            booking = db.query(Booking).with_for_update().filter(
                Booking.ID == booking_id
            ).first()

            if not booking:
                raise HTTPException(status_code=404, detail="预约记录不存在")

            # 权限检查
            if user["user_type"] == UserType.PATIENT.value and user["user_ref_id"] != booking.patient_ID:
                raise HTTPException(status_code=403, detail="无权取消他人的预约")

            # 查询对应的排班记录
            schedule = db.query(DocSchedule).filter(
                DocSchedule.doctor_id == booking.doctor_ID,
                DocSchedule.schedule_date == booking.book_date,
                DocSchedule.department == booking.department
            ).first()

            if not schedule:
                raise HTTPException(status_code=404, detail="对应的排班记录不存在")

            # 恢复号源（可加日志打印）
            print(f"取消前余号 - AM: {schedule.am_remaining}, PM: {schedule.pm_remaining}")
            if booking.book_time == "AM":
                schedule.am_remaining += 1
            elif booking.book_time == "PM":
                schedule.pm_remaining += 1
            print(f"取消后余号 - AM: {schedule.am_remaining}, PM: {schedule.pm_remaining}")

            # 删除对应的支付记录（预约支付）
            payment = db.query(Payment).filter(
                Payment.type == PaymentType.BOOK,
                Payment.ref_ID == booking.ID
            ).first()
            if payment:
                db.delete(payment)

            # 删除预约记录
            db.delete(booking)
            # 事务块结束自动commit

        return {"message": "预约取消成功"}

    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=f"取消预约失败: {str(e)}")



@router.get("/patient_bookings/{patient_id}/unpaid", response_model=List[PatientBookingResponse])
def get_patient_unpaid_bookings(
    patient_id: int,
    db: Session = Depends(get_db),
    user: dict = Depends(require_user(UserType.PATIENT, UserType.ADMIN))
):
    """
    根据患者ID获取所有未支付的预约记录。
    患者只能查看自己的未支付预约，管理员可以查看所有患者的。
    """
    try:
        # 权限检查：患者只能查看自己的记录
        if user["user_type"] == UserType.PATIENT.value and user["user_ref_id"] != patient_id:
            raise HTTPException(status_code=403, detail="无权查看其他患者的未支付预约记录")

        # 查询未支付的预约记录
        results = (
            db.query(Booking, Payment)
            .outerjoin(Payment, and_(Payment.ref_ID == Booking.ID, Payment.type == PaymentType.BOOK))
            .options(joinedload(Booking.doctor))
            .filter(
                Booking.patient_ID == patient_id,
                or_(Payment.pay_y_n == PaymentStatus.N, Payment.ID == None)  # 未支付或无支付记录
            )
            .all()
        )

        return [
            PatientBookingResponse(
                id=booking.ID,
                book_date=booking.book_date,
                book_time=booking.book_time,
                doctor_id=booking.doctor_ID,
                doctor_name=booking.doctor.name if booking.doctor else "未知医生",
                doctor_image_url=booking.doctor.image_url if booking.doctor else "",
                department=booking.department,
                status="未支付",
                ref_ID=payment.ID if payment else -1,
                whether_done=booking.whether_done
            )
            for booking, payment in results
        ]

    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取未支付预约记录失败: {str(e)}")






####医生查看自身预约任务+返回对应病人的详细信息
@router.get("/doctor_daily_bookings", response_model=List[DoctorDailyBookingResponse])
def get_doctor_daily_bookings(
    doctor_id: int,
    date: date,
    db: Session = Depends(get_db),
    user: dict = Depends(require_user(UserType.DOCTOR, UserType.ADMIN))  # 仅医生和管理员可访问
):
    try:
        # 权限检查：医生只能查看自己的预约
        if user["user_type"] == UserType.DOCTOR.value and user["user_ref_id"] != doctor_id:
            raise HTTPException(status_code=403, detail="无权查看其他医生的预约")
        
        # 查询当日预约并关联病人信息
        payment_exists = ( #先获取已支付了的记录
            db.query(Payment)
            .filter(
                Payment.type == PaymentType.BOOK,
                Payment.ref_ID == Booking.ID,
                Payment.pay_y_n == PaymentStatus.Y
            )
            .exists()
        )

        bookings = (
            db.query(Booking)
            .options(joinedload(Booking.patient))
            .filter(
                Booking.doctor_ID == doctor_id,
                Booking.book_date == date,
                payment_exists  # 添加这一句：只取已支付的预约
            )
            .order_by(Booking.book_time)
            .all()
        )
        
        if not bookings:  # 检查查询结果是否为空
            # raise HTTPException(status_code=404, detail=f"未查询到{date}该时间段内该医生的预约信息")
            return []

        response = []
        for booking in bookings:
            if not booking.patient:  # 检查关联的病人是否存在
                continue  # 或返回错误信息
            # 获取关联的病人对象
            patient = db.query(Patient).filter(Patient.ID == booking.patient_ID).first()
            if not patient:
                raise HTTPException(status_code=404, detail=f"未找到ID为{booking.patient_ID}的病人信息")
            
            response.append(DoctorDailyBookingResponse(
                id=booking.ID,
                patient_id=booking.patient_ID,
                book_time=booking.book_time,
                department=booking.department,
                status="已预约",
                whether_done=booking.whether_done if hasattr(booking, 'whether_done') else "N",
                patient_detail=PatientDetailResponse.from_orm(patient)  # 使用from_orm转换
            ))
        
        return response
        
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))
    

###医生写入是否已经处理预约
@router.patch("/bookings/{booking_id}/whether_done")
def update_booking_status(
    booking_id: int,
    status: WhetherDone,  # 直接接收Y/N
    db: Session = Depends(get_db),
    user: dict = Depends(require_user(UserType.DOCTOR, UserType.ADMIN))  # 允许医生和管理员
):
    """
    更新预约完成状态 (Y/N)
    - 医生只能更新自己的预约
    - 管理员可以更新任何预约
    """
    booking = db.query(Booking).filter(Booking.ID == booking_id).first()
    if not booking:
        raise HTTPException(status_code=404, detail="预约不存在")
    
    # 权限检查逻辑修改
    if (user["user_type"] == UserType.DOCTOR.value and 
        booking.doctor_ID != user["user_ref_id"]):
        raise HTTPException(
            status_code=403,
            detail="医生只能修改自己的预约记录"
        )
    
    booking.whether_done = status.value
    db.commit()
    return {
        "message": "状态更新成功",
        "new_status": status.value,
        "updated_by": "管理员" if user["user_type"] == UserType.ADMIN.value else "医生"
    }  